use axum::body::Body;
use axum::http::Response;
use axum::{
    Router,
    extract::Query,
    response::{Html, IntoResponse},
    routing::get,
};
use image::Luma;
use qrcode::QrCode;
use serde::Serialize;
use std::io::Cursor;
use std::{
    fs,
    time::UNIX_EPOCH,
};
use tokio::net::TcpListener;
use tower_http::services::ServeDir;

#[derive(Debug, Serialize)]
struct EntryInfo {
    name: String,
    is_dir: bool,
    full_path: String,
    create_time: u128,
}

async fn list_dir(
    Query(params): Query<std::collections::HashMap<String, String>>,
) -> impl IntoResponse {
    let mut dir = params
        .get("dir")
        .cloned()
        .unwrap_or_else(|| ".".to_string());
    let root_path = std::env::current_exe()
        .unwrap()
        .parent()
        .unwrap()
        .display()
        .to_string();
    println!("list dir {}", dir);
    if dir == "." || dir == "" {
        dir = std::env::current_exe()
            .unwrap()
            .parent()
            .unwrap()
            .to_str()
            .unwrap()
            .to_string();
    } else {
        dir = format!("{}{}", root_path, dir.replace(root_path.as_str(), ""));
    }

    // dir = dir + "/files";
    println!("list dir static {}", dir);
    let mut entries = vec![];
    if let Ok(read_dir) = fs::read_dir(&dir) {
        for entry in read_dir.flatten() {
            let file_type = entry.file_type().unwrap();
            let metadata = entry.metadata().unwrap();
            let created = metadata.created().unwrap();
            let ct = created.duration_since(UNIX_EPOCH).unwrap().as_millis();
            entries.push(EntryInfo {
                name: entry.file_name().to_string_lossy().to_string(),
                is_dir: file_type.is_dir(),
                full_path: entry
                    .path()
                    .to_str()
                    .unwrap()
                    .to_string()
                    .replace(root_path.as_str(), ""),
                create_time: ct,
            });
        }
    }

    entries.sort_by(|a, b| b.create_time.cmp(&a.create_time));

    // axum::Json(entries)
    axum::Json(serde_json::json!({
        "list": entries,
        "parent_path": dir.replace(root_path.as_str(), ""),
    }))
}

async fn index() -> impl IntoResponse {
    Html(include_str!("../static/index.html"))
}

async fn qr(Query(params): Query<std::collections::HashMap<String, String>>) -> impl IntoResponse {
    let path = params
        .get("path")
        .cloned()
        .unwrap_or_else(|| "".to_string());
    // 获取本机局域网ip
    let ip = local_ipaddress::get().unwrap_or_else(|| "127.0.0.1".to_string());
    let port = 3000;
    let url = format!("http://{}:{}/files{}", ip, port, path);
    println!("qr {}", url);
    let code = QrCode::new(url).unwrap();
    let image = code.render::<Luma<u8>>().build();
    let mut buf = Cursor::new(vec![]);
    image::DynamicImage::ImageLuma8(image)
        .write_to(&mut buf, image::ImageOutputFormat::Png)
        .unwrap();
    Response::builder()
        .header("Content-Type", "image/png")
        .body(Body::from(buf.into_inner()))
        .unwrap()
}

#[tokio::main]
async fn main() {
    let path = std::env::current_exe()
        .unwrap()
        .parent()
        .unwrap()
        .display()
        .to_string();
    println!("path {}", path);
    let app = Router::new()
        .route("/", get(index))
        .route("/api/list", get(list_dir))
        .route("/api/qr", get(qr))
        .nest_service("/files", ServeDir::new(path));

    let listener = TcpListener::bind("0.0.0.0:3000");

    match listener.await {
        Ok(l) => {
            println!("✅ Server started successfully");
            println!("🌐 Access URL: http://localhost:3000");

            let _ = axum::serve(l, app).await;
        }
        Err(e) => {
            eprintln!("❌ Server failed to start: {:?}", e);
            std::process::exit(1);
        }
    }
}
